﻿// ==================================================
// Copyright © 2016 杭州伯庸科技 版权所有
//
// 文 件 名：ModelAjust
// 功能描述：
//
// 创建标识：Yu.Liang(俞 亮) 2018/3/19 15:32:24
//
// 修改标识：
// 修改描述：
// ==================================================

using SuperMap.Data;
using SuperMap.Realspace;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using Shine.Framework;

namespace Shine.SchemaManager
{
    internal class ModelAjust : BaseCommand
    {
        private Scene m_Scene = null;
        private Workspace m_Workspace = null;
        private IApplication m_Application = null;

        #region 模型调整
        private Layer3DSetting lyrSetting = null;
        private List<int> idsHide = new List<int>();// 被选中的模型临时存储对象
        // 被选中的模型要素集临时存储对象
        private IList<Selection3D> GlobalSelections = new List<Selection3D>();
        private Layer3DDataset lyr3DDs;
        private int sid;

        private FrmModifyH frm = null;
        private int trackingLyrIdx = -1;   // trackinglayer上当前对象的index
        private Geometry selectedModel = null; // 当前被选中的模型
        private List<int> hideModelIds = null;

        // 记录模型当前的长宽高
        private double lengthFieldValue = 0;
        private double widthFieldValue = 0;
        private double heightFieldValue = 0;

        // 记录模型当前的position
        private double positionX = 0;
        private double positionY = 0;
        private double positionZ = 0;

        // 记录当前方案对比的模型图层名
        private string firstSchemaName = string.Empty;
        private string secondSchemaName = string.Empty;
        #endregion


        public ModelAjust()
        {
            base.m_caption = "模型调整";
            base.m_name = "ModelAjust";
            base.m_toolTip = "模型调整";
            base.m_message = "模型调整";
            base.m_category = "modelajust";
        }

        public override void OnCreate(object hook)
        {
            if (m_Application == null)
            {
                m_Application = hook as IApplication;
                m_Scene = (m_Application as IFramework).SceneControl.Scene;
                m_Workspace = m_Scene.Workspace;
            }
        }

        public override void OnClick()
        {
            if (m_Scene != null)
            {
                if (m_Scene.FindSelection(true).Count() == 0 )
                {
                    MessageBox.Show("请选择一个需要调整的模型!");
                    return;
                }

                if (m_Scene.FindSelection(true).Count() > 1)
                {
                    MessageBox.Show(string.Format("只针对单个模型进行调整，不支持批量模型调整!"));
                    return;
                }

                if (m_Scene.FindSelection(true)[0].Count > 1)
                {
                    MessageBox.Show(string.Format("只针对单个模型进行调整，不支持批量模型调整!"));
                    return;
                }

                this.LoadForm();

            }
            else
            {
                MessageBox.Show("请先加载三维场景，再激活项目管理面板.");
                return;
            }
        }

        private void LoadForm()
        {
            // 每次只针对一个模型
            Selection3D[] selections = this.m_Scene.FindSelection(true);
            Selection3D selection = selections[0];
            sid = selection[0];
            lyr3DDs = selection.Layer as Layer3DDataset;
            Recordset record = selection.ToRecordset();
            bool bl = record.SeekID(sid);
            if (!bl)
            {
                MessageBox.Show("没有选中有效的模型,请重新选择!");
                return;
            }

            // 获取被选中模型的对象
            selectedModel = record.GetGeometry();
            Feature feature = record.GetFeature();

            #region 用BoundingBox的尺寸代替模型的长宽高
            Point3D lowerPt = ((Geometry3D)selectedModel).BoundingBox.Lower;
            Point3D upperPt = ((Geometry3D)selectedModel).BoundingBox.Upper;

            // 把地理坐标系转到投影坐标系
            Point2Ds pt2ds = new Point2Ds();
            Point2D ptLower = new Point2D(lowerPt.X, lowerPt.Y);
            Point2D ptUpper = new Point2D(upperPt.X, upperPt.Y);
            pt2ds.Add(ptLower);
            pt2ds.Add(ptUpper);

            PrjCoordSys prj = new PrjCoordSys(PrjCoordSysType.China20003DegreeGk40N);
            bl = CoordSysTranslator.Forward(pt2ds, prj);
            if (!bl)
            {
                return;
            }

            lengthFieldValue = Math.Abs(pt2ds[1].X - pt2ds[0].X);
            widthFieldValue = Math.Abs(pt2ds[1].Y - pt2ds[0].Y);
            heightFieldValue = Math.Abs(upperPt.Z - lowerPt.Z);
            #endregion

            // 获取模型对象的position值
            this.positionX = ((Geometry3D)selectedModel).Position.X;
            this.positionY = ((Geometry3D)selectedModel).Position.Y;
            this.positionZ = ((Geometry3D)selectedModel).Position.Z;

            // 初始化面板
            frm = FrmModifyH.GetInstance();
            this.InitializeForm();
            frm.ModelLength = lengthFieldValue;
            frm.ModelWidth = widthFieldValue;
            frm.ModelHeight = heightFieldValue;
            frm.Show();

        }

        private void InitializeForm()
        {
            // 注册updown事件
            frm.changeAngle += this.ChangeModelAngle;
            frm.changeModelShapeLength += this.ChangeModelLenght;
            frm.changeModelShapeWidth += this.ChangeModelWidth;
            frm.changeModelShapeHeight += this.ChangeModeHeight;
            frm.changeModelPositionEast += this.ChangeModePosEast;
            frm.changeModelPositionSouth += this.ChangeModePosSouth;
            frm.changeModelPositionHeight += this.ChangeModePosHeight;
            frm.changeModelShapeAllScale += this.ChangeModelAllScale;
            frm.SetModelObjectVisible += this.SetObjectVisible;
            frm.SetModelObjectInvisible += this.SetObjectInvisible;
            frm.SetParamNull += this.SetParamNull;
        }

        // 模型角度变化
        private void ChangeModelAngle(double angle)
        {
            GeoStyle3D style3d = new GeoStyle3D();
            style3d.AltitudeMode = AltitudeMode.Absolute;
            double bottomHeight = ((Layer3DSettingVector)lyrSetting).Style.BottomAltitude;
            style3d.BottomAltitude = bottomHeight;
            // 更新角度值，每次更新的角度值都是针对原始模型的状态
            Geometry geo = null;
            if (this.trackingLyrIdx > -1)
            {
                geo = this.m_Scene.TrackingLayer.Get(this.trackingLyrIdx);
            }
            else
            {
                geo = this.selectedModel;
            }

            double currentAngle = ((Geometry3D)this.selectedModel).RotationZ;
            double newAngle = currentAngle + angle;
            if (newAngle >= 360)
            {
                newAngle = newAngle - 360;
            }

            ((Geometry3D)geo).RotationZ = newAngle;
            ((Geometry3D)geo).Style3D = style3d;
            this.m_Scene.TrackingLayer.Set(trackingLyrIdx, geo);
            this.m_Scene.Refresh();
        }

        /// <summary>
        /// 模型长度变化
        /// </summary>
        /// <param name="length"></param>
        private void ChangeModelLenght(double length)
        {
            GeoStyle3D style3d = new GeoStyle3D();
            style3d.AltitudeMode = AltitudeMode.Absolute;
            double bottomHeight = ((Layer3DSettingVector)lyrSetting).Style.BottomAltitude;
            style3d.BottomAltitude = bottomHeight;
            // 更新宽度值
            double newLengthFieldValue = length;
            Geometry geo = null;
            if (this.trackingLyrIdx > -1)
            {
                geo = this.m_Scene.TrackingLayer.Get(this.trackingLyrIdx);
            }
            else
            {
                geo = this.selectedModel;
            }
            double scale = 0;
            scale = Math.Abs(newLengthFieldValue / this.lengthFieldValue);
            ((Geometry3D)geo).ScaleX = scale;
            ((Geometry3D)geo).Style3D = style3d;
            if (this.trackingLyrIdx > -1)
            {
                this.m_Scene.TrackingLayer.Set(trackingLyrIdx, geo);
            }
            this.m_Scene.Refresh();
        }

        /// <summary>
        /// 模型高度变化
        /// </summary>
        /// <param name="height"></param>
        private void ChangeModeHeight(double height)
        {
            GeoStyle3D style3d = new GeoStyle3D();
            style3d.AltitudeMode = AltitudeMode.Absolute;
            double bottomHeight = ((Layer3DSettingVector)lyrSetting).Style.BottomAltitude;
            style3d.BottomAltitude = bottomHeight;
            double btHeight = ((Layer3DSettingVector)this.lyr3DDs.AdditionalSetting).Style.BottomAltitude;
            style3d.BottomAltitude = btHeight;
            // 更新高度值
            double newHeightFieldValue = height;
            Geometry geo = null;
            if (this.trackingLyrIdx > -1)
            {
                geo = this.m_Scene.TrackingLayer.Get(this.trackingLyrIdx);
            }
            else
            {
                geo = this.selectedModel;
            }

            double scale = 0;
            double scaleChange = 0;
            scaleChange = newHeightFieldValue - this.heightFieldValue;
            scale = Math.Abs(newHeightFieldValue / this.heightFieldValue);
            ((Geometry3D)geo).ScaleZ = scale;
            ((Geometry3D)geo).Style3D = style3d;

            // 保持底座位置不变
            Point3D pt3D = new Point3D();
            pt3D.X = ((Geometry3D)geo).Position.X;
            pt3D.Y = ((Geometry3D)geo).Position.Y;

            // 每次改变都是以原始模型位置为基准
            if (scaleChange < 0) // scaleChange是负数，模型缩小，中心点需要下降
            {
                pt3D.Z = ((Geometry3D)selectedModel).Position.Z + scaleChange / 2; // 中心点下降
            }
            else
            {
                pt3D.Z = ((Geometry3D)selectedModel).Position.Z + Math.Abs(scaleChange / 2); // 中心点上升
            }

            //pt3D.Z = ((Geometry3D)selectedModel).Position.Z;
            ((Geometry3D)geo).Position = pt3D;


            if (this.trackingLyrIdx > -1)
            {
                this.m_Scene.TrackingLayer.Set(trackingLyrIdx, geo);
            }
            this.m_Scene.Refresh();
        }

        /// <summary>
        /// 模型宽度变化
        /// </summary>
        /// <param name="width"></param>
        private void ChangeModelWidth(double width)
        {
            GeoStyle3D style3d = new GeoStyle3D();
            style3d.AltitudeMode = AltitudeMode.Absolute;
            double bottomHeight = ((Layer3DSettingVector)lyrSetting).Style.BottomAltitude;
            style3d.BottomAltitude = bottomHeight;
            // 更新宽度值
            double newWidthFieldValue = width;
            Geometry geo = null;
            if (this.trackingLyrIdx > -1)
            {
                geo = this.m_Scene.TrackingLayer.Get(this.trackingLyrIdx);
            }
            else
            {
                geo = this.selectedModel;
            }
            double scale = 0;
            scale = Math.Abs(newWidthFieldValue / this.widthFieldValue);
            ((Geometry3D)geo).ScaleY = scale;
            ((Geometry3D)geo).Style3D = style3d;
            if (this.trackingLyrIdx > -1)
            {
                this.m_Scene.TrackingLayer.Set(trackingLyrIdx, geo);
            }
            this.m_Scene.Refresh();
        }

        // 将当前模型隐藏，同时在trackinglayer里增加一个对象
        private void SetObjectInvisible()
        {
            lyrSetting = this.lyr3DDs.AdditionalSetting as Layer3DSettingVector;
            double height = ((Layer3DSettingVector)lyrSetting).Style.BottomAltitude;
            this.lyr3DDs.SetObjectsVisible(new List<int> { this.sid }, false);
            this.m_Scene.TrackingLayer.IsVisible = true;
            GeoStyle3D style3d = new GeoStyle3D();
            style3d.AltitudeMode = AltitudeMode.Absolute;
            style3d.BottomAltitude = height;
            ((Geometry3D)selectedModel).Style3D = style3d;
            if (trackingLyrIdx < 0)
            {
                trackingLyrIdx = this.m_Scene.TrackingLayer.Add(selectedModel, "tracklayerObj");
            }
        }

        // 删除trackinglayer的对象，同时更新当前模型
        private void SetObjectVisible()
        {
            DialogResult dr = MessageBox.Show("确定更新模型？", "提示：", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
            if (dr == System.Windows.Forms.DialogResult.No)
            { return; }
            DatasetVector dsVector = lyr3DDs.Dataset as DatasetVector;
            Recordset rsCord = dsVector.GetRecordset(false, CursorType.Dynamic);
            // 获取选中的模型
            rsCord.SeekID(this.sid);
            Geometry geo = null;
            if (this.trackingLyrIdx > -1)
            {
                geo = this.m_Scene.TrackingLayer.Get(this.trackingLyrIdx);
                this.m_Scene.TrackingLayer.Remove(this.trackingLyrIdx);
                this.trackingLyrIdx = -1;
            }
            Geometry3D geoModel = geo as Geometry3D;
            rsCord.Edit();
            rsCord.SetGeometry(geoModel);
            rsCord.Update();

            // 获取模型对象的position值
            this.positionX = ((Geometry3D)geoModel).Position.X;
            this.positionY = ((Geometry3D)geoModel).Position.Y;
            this.positionZ = ((Geometry3D)geoModel).Position.Z;

            // 获取当前选中的数据集
            this.lyr3DDs.SetObjectsVisible(new List<int> { -1 }, false);
            rsCord.Dispose();
            //this.lyr3DDs.UpdateData();
            //this.this.Focus();
        }

        // 模型调整界面退出时触发
        private void SetParamNull()
        {
            // 模型的变化复原
            if (this.trackingLyrIdx > -1)
            {
                this.m_Scene.TrackingLayer.Remove(this.trackingLyrIdx);
            }

            this.lyr3DDs.SetObjectsVisible(new List<int> { -1 }, false);

            lyr3DDs = null;
            sid = 0;
            trackingLyrIdx = -1;
            selectedModel = null; // 当前被选中的模型

            lengthFieldValue = 0;
            widthFieldValue = 0;
            heightFieldValue = 0;

            // 记录模型当前的position
            positionX = 0;
            positionY = 0;
            positionZ = 0;
        }

        /// <summary>
        /// 模型向东移动
        /// </summary>
        /// <param name="distance"></param>
        private void ChangeModePosEast(double distance)
        {
            GeoStyle3D style3d = new GeoStyle3D();
            style3d.AltitudeMode = AltitudeMode.Absolute;
            double bottomHeight = ((Layer3DSettingVector)lyrSetting).Style.BottomAltitude;
            style3d.BottomAltitude = bottomHeight;
            // 更新值
            double dist = distance;
            Geometry geo = null;
            if (this.trackingLyrIdx > -1)
            {
                geo = this.m_Scene.TrackingLayer.Get(this.trackingLyrIdx);
            }
            else
            {
                geo = this.selectedModel;
            }

            Point3D pt3D = new Point3D();
            pt3D.X = this.positionX;
            pt3D.Y = ((Geometry3D)geo).Position.Y; // Y方向采用模型当前的点位
            pt3D.Z = ((Geometry3D)geo).Position.Z;

            // TrackingLayer只支持地理坐标系，需要把dist转换成经纬度值
            // 把地理坐标系转到投影坐标系
            Point2Ds pt2ds = new Point2Ds();
            Point2D pt2d = new Point2D(pt3D.X, pt3D.Y);
            pt2ds.Add(pt2d);
            Console.Write("偏移前坐标：{0} - {1}", pt3D.X, pt3D.Y);
            Console.WriteLine();

            PrjCoordSys prj = new PrjCoordSys(PrjCoordSysType.China20003DegreeGk40N);
            bool bl = CoordSysTranslator.Forward(pt2ds, prj);
            if (!bl)
            {
                return;
            }
            Console.Write("转成投影坐标：{0} - {1}", pt2ds[0].X, pt2ds[0].Y);
            Console.WriteLine();

            // 把X坐标偏移值加上去
            pt2d.X = pt2ds[0].X + dist;
            pt2d.Y = pt2ds[0].Y;
            pt2ds.Clear();
            pt2ds.Add(pt2d);
            Console.Write("加上偏移值的投影坐标：{0} - {1}", pt2ds[0].X, pt2ds[0].Y);
            Console.WriteLine();

            bl = CoordSysTranslator.Inverse(pt2ds, prj);
            if (!bl)
            {
                return;
            }
            Console.Write("重新转换后的地理坐标：{0} - {1}", pt2ds[0].X, pt2ds[0].Y);
            Console.WriteLine();

            pt3D.X = pt2ds[0].X;
            ((Geometry3D)geo).Position = pt3D;

            if (this.trackingLyrIdx > -1)
            {
                this.m_Scene.TrackingLayer.Set(trackingLyrIdx, geo);
            }
            this.m_Scene.Refresh();

        }

        /// <summary>
        /// 模型向南移动
        /// </summary>
        /// <param name="distance"></param>
        private void ChangeModePosSouth(double distance)
        {
            GeoStyle3D style3d = new GeoStyle3D();
            style3d.AltitudeMode = AltitudeMode.Absolute;
            double bottomHeight = ((Layer3DSettingVector)lyrSetting).Style.BottomAltitude;
            style3d.BottomAltitude = bottomHeight;
            // 更新值
            double dist = distance;
            Geometry geo = null;
            if (this.trackingLyrIdx > -1)
            {
                geo = this.m_Scene.TrackingLayer.Get(this.trackingLyrIdx);
            }
            else
            {
                geo = this.selectedModel;
            }

            Point3D pt3D = new Point3D();
            pt3D.X = ((Geometry3D)geo).Position.X;
            pt3D.Y = this.positionY;
            pt3D.Z = ((Geometry3D)geo).Position.Z;

            // TrackingLayer只支持地理坐标系，需要把dist转换成经纬度值
            // 把地理坐标系转到投影坐标系
            Point2Ds pt2ds = new Point2Ds();
            Point2D pt2d = new Point2D(pt3D.X, pt3D.Y);
            pt2ds.Add(pt2d);
            Console.Write("偏移前坐标：{0} - {1}", pt3D.X, pt3D.Y);
            Console.WriteLine();

            PrjCoordSys prj = new PrjCoordSys(PrjCoordSysType.China20003DegreeGk40N);
            bool bl = CoordSysTranslator.Forward(pt2ds, prj);
            if (!bl)
            {
                return;
            }
            Console.Write("转成投影坐标：{0} - {1}", pt2ds[0].X, pt2ds[0].Y);
            Console.WriteLine();

            // 把Y坐标偏移值加上去
            pt2d.Y = pt2ds[0].Y + dist;
            pt2d.X = pt2ds[0].X;
            pt2ds.Clear();
            pt2ds.Add(pt2d);
            Console.Write("加上偏移值的投影坐标：{0} - {1}", pt2ds[0].X, pt2ds[0].Y);
            Console.WriteLine();

            bl = CoordSysTranslator.Inverse(pt2ds, prj);
            if (!bl)
            {
                return;
            }
            Console.Write("重新转换后的地理坐标：{0} - {1}", pt2ds[0].X, pt2ds[0].Y);
            Console.WriteLine();

            pt3D.Y = pt2ds[0].Y;
            ((Geometry3D)geo).Position = pt3D;

            if (this.trackingLyrIdx > -1)
            {
                this.m_Scene.TrackingLayer.Set(trackingLyrIdx, geo);
            }
            this.m_Scene.Refresh();
        }

        /// <summary>
        /// 模型垂直高度变化
        /// </summary>
        /// <param name="distance"></param>
        private void ChangeModePosHeight(double distance)
        {
            GeoStyle3D style3d = new GeoStyle3D();
            style3d.AltitudeMode = AltitudeMode.Absolute;
            double bottomHeight = ((Layer3DSettingVector)lyrSetting).Style.BottomAltitude;
            style3d.BottomAltitude = bottomHeight;
            // 更新值
            double dist = distance;
            Geometry geo = null;
            if (this.trackingLyrIdx > -1)
            {
                geo = this.m_Scene.TrackingLayer.Get(this.trackingLyrIdx);
            }
            else
            {
                geo = this.selectedModel;
            }

            Point3D pt3D = new Point3D();
            pt3D.X = ((Geometry3D)geo).Position.X;
            pt3D.Y = ((Geometry3D)geo).Position.Y;
            pt3D.Z = this.positionZ + dist;
            ((Geometry3D)geo).Position = pt3D;

            if (this.trackingLyrIdx > -1)
            {
                this.m_Scene.TrackingLayer.Set(trackingLyrIdx, geo);
            }
            this.m_Scene.Refresh();
        }

        /// <summary>
        /// 等比例变化时，控制几个updown控件的状态
        /// </summary>
        /// <param name="scale"></param>
        private void ChangeModelAllScale(double scale)
        {
            GeoStyle3D style3d = new GeoStyle3D();
            style3d.AltitudeMode = AltitudeMode.Absolute;
            double bottomHeight = ((Layer3DSettingVector)lyrSetting).Style.BottomAltitude;
            style3d.BottomAltitude = bottomHeight;
            Geometry geo = null;
            if (this.trackingLyrIdx > -1)
            {
                geo = this.m_Scene.TrackingLayer.Get(this.trackingLyrIdx);
            }
            else
            {
                geo = this.selectedModel;
            }

            ((Geometry3D)geo).ScaleX = scale;
            ((Geometry3D)geo).ScaleY = scale;
            ((Geometry3D)geo).ScaleZ = scale;
            ((Geometry3D)geo).Style3D = style3d;

            // 根据缩放的比例确定底部中心点的Z值,保持底座位置不变
            Point3D pt3D = new Point3D();
            pt3D.X = ((Geometry3D)geo).Position.X;
            pt3D.Y = ((Geometry3D)geo).Position.Y;

            double scaleChange = 0;
            if (scale > 1)
            {
                // 模型放大
                scaleChange = this.heightFieldValue * (scale - 1);
                pt3D.Z = ((Geometry3D)selectedModel).Position.Z + Math.Abs(scaleChange / 2); // 中心点上升
            }
            else
            {
                // 模型缩小
                scaleChange = this.heightFieldValue * (1 - scale);
                pt3D.Z = ((Geometry3D)selectedModel).Position.Z - scaleChange / 2; // 中心点下降
            }

            ((Geometry3D)geo).Position = pt3D;

            if (this.trackingLyrIdx > -1)
            {
                this.m_Scene.TrackingLayer.Set(trackingLyrIdx, geo);
            }
            this.m_Scene.Refresh();
        }

        public override int Bitmap
        {
            get
            {
                string imgFile = Application.StartupPath + "\\Images\\" + GetType().Name + ".png";

                if (System.IO.File.Exists(imgFile) == false) return 0;

                return new Bitmap(Image.FromFile(imgFile)).GetHbitmap().ToInt32();
            }
        }

        public override bool Enabled
        {
            get {
                if (m_Scene == null)
                    return false;
                else
                    return true;
            }
        }
    }
}
